﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPMS.Utility;
using SPMS.Log.ObjectFormatter;

namespace SPMS.Log.Utility
{
    /// <summary>
    /// 模板解析器
    /// </summary>
    /// <remarks>
    /// 用于将给定的命名属性提供者按照给定的模板解析为字符串
    /// </remarks>
    /// <history>
    /// [zanezeng]               2009/10/20 10:45    创建
    /// </history>
    public sealed class PatternParser : IPatternParser
    {
        #region ---Constructor

        /// <summary>
        /// 初始化类型<see cref="PatternParser"/>的新实例
        /// </summary>
        /// <param name="pattern">用于解析命名属性提供者的模板</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="pattern"/>为<c>Null</c>或者为空字符串</exception>
        /// <history>
        /// [zanezeng]               2009/10/20 10:46    创建
        /// </history>
        public PatternParser( string pattern )
            : this( pattern, null )
        {
        }

        /// <summary>
        /// 初始化类型<see cref="PatternParser"/>的新实例
        /// </summary>
        /// <param name="pattern">用于解析命名属性提供者的模板</param>
        /// <param name="provider">对象格式化器提供者</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="pattern"/>为<c>Null</c>或者为空字符串</exception>
        /// <history>
        /// [zanezeng]               2009/10/20 10:46    创建
        /// </history>
        public PatternParser( string pattern, IObjectFormatterProvider provider )
        {
            //断言参数
            ArgumentAssert.AssertStringArgumentNotNullOrEmpty( pattern, "pattern" );

            //保存参数
            this.m_Pattern = pattern;
            this.m_Provider = provider;

            //初始化
            this.InitPatternItems();
        }

        /// <summary>
        /// 初始化类型<see cref="PatternParser"/>
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/21 10:42    创建
        /// </history>
        static PatternParser()
        {
            //初始化内置关键字
            InitBuildinKeywords();
        }

        #endregion

        #region ---IPatternParser

        /// <summary>
        /// 将给定的命名属性提供者解析为字符串
        /// </summary>
        /// <param name="provider">要解析为字符串的命名属性提供者</param>
        /// <param name="formatProvider">一个<see cref="IFormatProvider"/>，它提供区域性特定的格式设置信息</param>
        /// <returns>定的命名属性提供者的字符串描述</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/20 10:42    创建
        /// </history>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/20 10:53    创建
        /// </history>
        public string Parse( INamedPropertyProvider provider, IFormatProvider formatProvider )
        {
            //获得模板项目值
            object[] values = this.GetPatternItemValues( provider, formatProvider );

            //返回格式化字符串
            if (null != formatProvider)
            {
                return string.Format( formatProvider, this.m_PatternFormat, values );
            }
            else
            {
                return string.Format( this.m_PatternFormat, values );
            }
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 初始化权限模板项目
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/20 12:20    创建
        /// </history>
        private void InitPatternItems()
        {
            //用于保存模板格式化字符串
            StringBuilder formatBuilder = new StringBuilder();

            //用于保存悬挂的模板内容
            StringBuilder patternBuilder = new StringBuilder();

            //用于保存模板项目
            List<PatternItem> patternItems = new List<PatternItem>();

            //用于保存当前字符
            char current;

            //用于保存下一个字符
            char? next;

            //用于记录模板的序号
            int patternIndex = 0;

            //用于记录是否有悬挂的左限定符
            bool isLeftDelimiter = false;

            //用于记录右限定符的悬挂个数
            int rightDelimiterCount = 0;

            //用于保存模板项目
            PatternItem pattern = null;

            //用于保存模板定义
            string patternDefinition = null;

            //循环解析模板
            for (int index = 0; index < this.m_Pattern.Length; index++)
            {
                //获得当前字符
                current = this.m_Pattern[index];

                //获得下一个字符
                if (index < this.m_Pattern.Length - 1)
                {
                    next = this.m_Pattern[index + 1];
                }
                else
                {
                    next = null;
                }

                //判断当前字符是否为左限定符
                if (current == LEFT_DELIMITER)
                {
                    //判断是否有下一个字符
                    if (null == next)
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsErrorEnabled)
                        {
                            LoggingContext.MetaLog.Error( "模板'{0}'无法解析，左限定符不匹配", this.m_Pattern );
                        }

                        //抛出异常
                        throw new InvalidOperationException( string.Format( Properties.Resources.MSG_PATTERN_LEFT_DELIMITER_MISMATCH, this.m_Pattern ) );
                    }

                    //判断下一个字符是否为左限定符
                    if (next.Value == LEFT_DELIMITER)
                    {
                        //判断是否悬挂左限定符
                        if (isLeftDelimiter)
                        {
                            //认为在模板中通配限定符
                            patternBuilder.Append( LEFT_DELIMITER );
                        }
                        else
                        {
                            //认为格式化字符串中通配限定符
                            formatBuilder.Append( LEFT_DELIMITER, 2 );
                        }

                        //跳过下一个字符
                        index += 1;

                        //继续执行循环
                        continue;
                    }

                    //判断是否悬挂左限定符
                    if (isLeftDelimiter)
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsErrorEnabled)
                        {
                            LoggingContext.MetaLog.Error( "模板'{0}'无法解析，右限定符不匹配", this.m_Pattern );
                        }

                        //抛出异常
                        throw new InvalidOperationException( string.Format( Properties.Resources.MSG_PATTERN_RIGHT_DELIMITER_MISMATCH, this.m_Pattern ) );
                    }

                    //设置悬挂左限定符
                    isLeftDelimiter = true;
                }
                //判断当前字符是否为右限定符
                else if (current == RIGHT_DELIMITER)
                {
                    //判断下一个字符是否为右限定符
                    if (null != next && next.Value == RIGHT_DELIMITER)
                    {
                        //判断是否悬挂左限定符
                        if (isLeftDelimiter)
                        {
                            //增加悬挂右限定符计数
                            rightDelimiterCount += 1;

                            //继续处理后续字符
                            continue;
                        }
                        else
                        {
                            //认为格式化字符串中通配限定符，向格式化字符串中添加限定符
                            formatBuilder.Append( RIGHT_DELIMITER, 2 );

                            //跳过下一个字符
                            index += 1;

                            //判断是否有后续的字符
                            if (index < this.m_Pattern.Length - 1)
                            {
                                //如果有后续字符，则继续执行循环
                                continue;
                            }
                        }
                    }

                    //判断是否悬挂左限定符
                    if (isLeftDelimiter)
                    {
                        //判断是否有悬挂的右限定符并且限定符的个数是否为奇数个
                        if (rightDelimiterCount > 0 && rightDelimiterCount % 2 != 0)
                        {
                            //如果有奇数个悬挂的右限定符，则认为为在属性模板中通配右限定符，添加右限定符
                            patternBuilder.Append( RIGHT_DELIMITER, (rightDelimiterCount + 1) / 2 );

                            //重置悬挂右限定符个数
                            rightDelimiterCount = 0;

                            //继续处理后续字符
                            continue;
                        }

                        //判断模板项目内容是否为空
                        if (0 == patternBuilder.Length)
                        {
                            //输出日志
                            if (LoggingContext.MetaLog.IsErrorEnabled)
                            {
                                LoggingContext.MetaLog.Error( "模板'{0}'无法解析，属性格式化信息为空", this.m_Pattern );
                            }

                            //抛出异常
                            throw new InvalidOperationException( string.Format( Properties.Resources.MSG_PATTERN_PROPERTY_EMPTY, this.m_Pattern ) );
                        }

                        //获得模板定义
                        patternDefinition = patternBuilder.ToString();

                        //清除悬挂的属性模板
                        patternBuilder.Remove( 0, patternBuilder.Length );

                        //解析模板项目
                        pattern = this.ResolvePatternItem( patternDefinition );

                        //保存模板项目
                        patternItems.Add( pattern );

                        //添加格式化表达式
                        formatBuilder.Append( LEFT_DELIMITER );
                        formatBuilder.Append( patternIndex );
                        formatBuilder.Append( RIGHT_DELIMITER );

                        //增加模板项目序号
                        patternIndex += 1;

                        //设置不悬挂左限定符
                        isLeftDelimiter = false;

                        //判断是否有悬挂的右限定符并且限定符的个数是否为偶数个
                        if (rightDelimiterCount > 0 && rightDelimiterCount % 2 == 0)
                        {
                            //如果有偶数个悬挂的右限定符，则认为为在格式化字符串中通配右限定符
                            formatBuilder.Append( RIGHT_DELIMITER, rightDelimiterCount );

                            //重置悬挂右限定符个数
                            rightDelimiterCount = 0;
                        }
                    }
                    else
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsErrorEnabled)
                        {
                            LoggingContext.MetaLog.Error( "模板'{0}'无法解析，左限定符不匹配", this.m_Pattern );
                        }

                        //抛出异常
                        throw new InvalidOperationException( string.Format( Properties.Resources.MSG_PATTERN_LEFT_DELIMITER_MISMATCH, this.m_Pattern ) );
                    }
                }
                else
                {
                    //判断是否悬挂左限定符
                    if (isLeftDelimiter)
                    {
                        //如果有悬挂的左限定符，则添加到模板项目中
                        patternBuilder.Append( current );
                    }
                    else
                    {
                        //如果没有悬挂的左限定符，则添加到模板格式化字符串中
                        formatBuilder.Append( current );
                    }
                }
            }

            //判断是否有悬挂的左限定符
            if (isLeftDelimiter)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error( "模板'{0}'无法解析，右限定符不匹配", this.m_Pattern );
                }

                //抛出异常
                throw new InvalidOperationException( string.Format( Properties.Resources.MSG_PATTERN_RIGHT_DELIMITER_MISMATCH, this.m_Pattern ) );
            }

            //设置模板格式化字符串
            this.m_PatternFormat = formatBuilder.ToString();

            //设置模板项目
            this.m_PatternItems = patternItems.ToArray();
        }

        /// <summary>
        /// 从给定的属性模板中解析出属性的名称和属性的格式化字符串
        /// </summary>
        /// <param name="pattern">要被解析的属性模板</param>
        /// <returns>从属性模板中解析出来的属性名称</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/20 15:29    创建
        /// </history>
        private PatternItem ResolvePatternItem( string pattern )
        {
            //创建模板项目
            PatternItem item = new PatternItem();

            //用于保存对齐格式化字符串
            string padding = null;

            //获得对齐分隔符的位置
            int paddingIndex = GetSeparatorIndex( pattern, SEPARATOR_ALIGN );

            //获得格式化分隔符的位置
            int formatIndex = GetSeparatorIndex( pattern, SEPARATOR_FORMAT );

            //判断是否有格式化字符串
            if (formatIndex > 0)
            {
                //判断对齐分隔符的索引位置是否大于格式化分隔符的索引位置
                if (paddingIndex > formatIndex)
                {
                    //如果对齐分隔符的索引位置是否大于格式化分隔符的索引位置，则认为没有对齐格式化字符串
                    paddingIndex = -1;
                }

                //判断是否有对齐格式化字符串
                if (paddingIndex > 0)
                {
                    //设置模板名称
                    item.Name = pattern.Substring( 0, paddingIndex );

                    //设置对齐格式化字符串
                    padding = pattern.Substring( paddingIndex + 1, formatIndex - paddingIndex - 1 );

                    //设置对齐值
                    item.Padding = ObjectUtility.ParseValue<int>( padding, 0, false );
                }
                else
                {
                    //设置模板名称
                    item.Name = pattern.Substring( 0, formatIndex );
                }

                //设置格式化字符串
                item.Format = pattern.Substring( formatIndex + 1, pattern.Length - formatIndex - 1 );
            }
            //判断是否有对齐格式化字符串
            else if (paddingIndex > 0)
            {
                //设置模板名称
                item.Name = pattern.Substring( 0, paddingIndex );

                //设置对齐格式化字符串
                padding = pattern.Substring( paddingIndex + 1, pattern.Length - paddingIndex - 1 );

                //设置对齐值
                item.Padding = ObjectUtility.ParseValue<int>( padding, 0, false );
            }
            else
            {
                //设置模板名称
                item.Name = pattern;
            }

            //返回模板对象
            return item;
        }

        /// <summary>
        /// 获得模板项目的值
        /// </summary>
        /// <param name="provider">要解析为字符串的命名属性提供者</param>
        /// <param name="formatProvider">一个<see cref="IFormatProvider"/>，它提供区域性特定的格式设置信息</param>
        /// <returns>模板项目的值</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/20 12:12    创建
        /// </history>
        private object[] GetPatternItemValues( INamedPropertyProvider provider, IFormatProvider formatProvider )
        {
            //创建用于保存模板项目值的数组
            object[] values = new object[this.m_PatternItems.Length];

            //用于保存模板项目的遏制
            object itemValue = null;

            //用于保存模板项目
            PatternItem item = null;

            //循环获得模板项目的值
            for (int index = 0; index < this.m_PatternItems.Length; index++)
            {
                //获得目标项目
                item = this.m_PatternItems[index];

                //判断是否为内置关键字
                if (BUILDIN_KEYWORDS.ContainsKey( item.Name ))
                {
                    //设置为内置关键字
                    values[index] = BUILDIN_KEYWORDS[item.Name];
                }
                else
                {
                    //获得模板项目的值
                    itemValue = provider[item.Name];

                    //设置对象的格式化值
                    values[index] = FormatPatternItemValue( itemValue, item, formatProvider );
                }
            }

            //返回模板项目的值
            return values;
        }

        /// <summary>
        /// 格式化给定模板项目的值
        /// </summary>
        /// <param name="obj">要格式化的对象值</param>
        /// <param name="item">要格式化的对象值所对应的模板项目</param>
        /// <param name="formatProvider">一个<see cref="IFormatProvider"/>，它提供区域性特定的格式设置信息</param>
        /// <returns>格式化后的对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/21 9:57    创建
        /// </history>
        private object FormatPatternItemValue( object obj, PatternItem item, IFormatProvider formatProvider )
        {
            //用于保存格式化的值
            string formattedObject = null;

            //判断是否设置了对象格式化提供者
            if (null == this.m_Provider)
            {
                //如果没有设置对象格式化提供者，则使用默认的对象格式化器格式化
                formattedObject = LoggingContext.ObjectFormatterRegistry.FormatObject( obj, item.Format, formatProvider );
            }
            else
            {
                //获得对象格式化器
                IObjectFormatter formatter = this.m_Provider.GetFormatter( obj.GetType() );

                //判断是否有对象格式化器
                if (null == formatter)
                {
                    //如果没有对象格式化器，则使用默认的对象格式化器格式化
                    formattedObject = LoggingContext.ObjectFormatterRegistry.FormatObject( obj, item.Format, formatProvider );
                }
                else
                {
                    //使用对象格式化器格式化对象
                    formattedObject = formatter.Format( this.m_Provider, obj, item.Format, formatProvider );
                }
            }

            //判断是否设定了左对齐
            if (item.Padding < 0)
            {
                //执行左对齐
                formattedObject = formattedObject.PadRight( Math.Abs( item.Padding ) );
            }
            //判断是否设定了右对齐
            else if (item.Padding > 0)
            {
                //执行右对齐
                formattedObject = formattedObject.PadLeft( item.Padding );
            }

            //返回格式化的结果
            return formattedObject;
        }

        /// <summary>
        /// 获得给定字符串中给定分隔符的位置
        /// </summary>
        /// <param name="source">用于检索分隔符位置的源字符串</param>
        /// <param name="separator">要检索的分隔符</param>
        /// <returns>分隔符的索引位置，如果不存在，则返回-1</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/21 9:38    创建
        /// </history>
        private static int GetSeparatorIndex( string source, char separator )
        {
            //获得索引位置
            int index = source.IndexOf( separator );

            //判断索引位置是否为0
            if (index == 0)
            {
                //判断是否越界
                if (index < source.Length - 1)
                {
                    //如果索引位置为0，则需要检索下一个分割符的索引位置
                    index = source.IndexOf( separator, index + 1 );
                }
                else
                {
                    //如果越界，则设置为-1
                    index = -1;
                }
            }

            //返回索引位置
            return index;
        }

        /// <summary>
        /// 初始化内置关键字
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/21 10:42    创建
        /// </history>
        private static void InitBuildinKeywords()
        {
            //添加换行关键字
            BUILDIN_KEYWORDS.Add( "NEWLINE", "\r\n" );

            //添加Tab关键字
            BUILDIN_KEYWORDS.Add( "TAB", "\t" );
        }

        #endregion

        #region ---Field

        /// <summary>
        /// 用于解析的模板
        /// </summary>
        private string m_Pattern;

        /// <summary>
        /// 对象格式化器提供者
        /// </summary>
        private IObjectFormatterProvider m_Provider;

        /// <summary>
        /// 模板的格式化字符串
        /// </summary>
        private string m_PatternFormat;

        /// <summary>
        /// 模板项目
        /// </summary>
        private PatternItem[] m_PatternItems;

        /// <summary>
        /// 左侧的限定符
        /// </summary>
        private const char LEFT_DELIMITER = '{';

        /// <summary>
        /// 右侧的限定符
        /// </summary>
        private const char RIGHT_DELIMITER = '}';

        /// <summary>
        /// 对齐设置分隔符
        /// </summary>
        private const char SEPARATOR_ALIGN = ',';

        /// <summary>
        /// 格式化设置分隔符
        /// </summary>
        private const char SEPARATOR_FORMAT = ':';

        /// <summary>
        /// 内置关键字
        /// </summary>
        private static readonly Dictionary<string, object> BUILDIN_KEYWORDS = new Dictionary<string, object>( StringComparer.InvariantCultureIgnoreCase );

        #endregion

        #region ---Class

        /// <summary>
        /// 模板项目
        /// </summary>
        /// <remarks>用于保存模板项目</remarks>
        /// <history>
        /// [zanezeng]               2009/10/21 9:26    创建
        /// </history>
        private sealed class PatternItem
        {
            #region ---Constructor

            /// <summary>
            /// 初始化类型<see cref="PatternItem"/>的新实例
            /// </summary>
            /// <remarks></remarks>
            /// <history>
            /// [zanezeng]               2009/10/21 9:31    创建
            /// </history>
            public PatternItem()
            {
            }

            /// <summary>
            /// 初始化类型<see cref="PatternItem"/>的新实例
            /// </summary>
            /// <param name="name">模板项目的名称</param>
            /// <param name="format">模板项目的格式化字符串</param>
            /// <remarks></remarks>
            /// <history>
            /// [zanezeng]               2009/10/21 9:27    创建
            /// </history>
            public PatternItem( string name, string format )
            {
                //保存参数
                this.m_Name = name;
                this.m_Format = format;
            }

            #endregion

            #region ---Property

            /// <summary>
            /// 获得或者设置模板项目的名称
            /// </summary>
            /// <value>模板项目的名称</value>
            /// <remarks></remarks>
            /// <history>
            /// [zanezeng]               2009/10/21 9:28    创建
            /// </history>
            public string Name
            {
                get
                {
                    return this.m_Name;
                }
                set
                {
                    this.m_Name = value;
                }
            }

            /// <summary>
            /// 获得或者设置模板项目的格式化字符串
            /// </summary>
            /// <value>模板项目的格式化字符串</value>
            /// <remarks></remarks>
            /// <history>
            /// [zanezeng]               2009/10/21 9:28    创建
            /// </history>
            public string Format
            {
                get
                {
                    return this.m_Format;
                }
                set
                {
                    this.m_Format = value;
                }
            }

            /// <summary>
            /// 获得或者设置模板项目的对齐设定
            /// </summary>
            /// <value>模板项目的对齐设定</value>
            /// <remarks></remarks>
            /// <history>
            /// [zanezeng]               2009/10/21 9:28    创建
            /// </history>
            public int Padding
            {
                get
                {
                    return this.m_Padding;
                }
                set
                {
                    this.m_Padding = value;
                }
            }

            #endregion

            #region ---Field

            /// <summary>
            /// 模板项目的名称
            /// </summary>
            private string m_Name;

            /// <summary>
            /// 模板项目的格式化字符串
            /// </summary>
            private string m_Format;

            /// <summary>
            /// 对齐设定
            /// </summary>
            private int m_Padding;

            #endregion
        }

        #endregion
    }
}
